package com.aij.starter.utils;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.sun.jna.Native;

/**
* 从jar包中加载指定路径下的动态库
*/
public class NativeUtils {
   private static final int BUFFER_SIZE = 8192;
   public static final String NATIVE_FOLDER_PATH_PREFIX = "nativeutils";

   /**
    * Temporary directory which will contain the DLLs.
    */
   private static File temporaryDir;

   /**
    * Private constructor - this class will never be instanced
    */
   private NativeUtils() {
   }

   /**
    * Loads library from current JAR archive
    * @see #copyToTempFromJar(String, Class) 
    */
   public static synchronized Object loadLibraryFromJar(String path, Class<?> loadClass) throws IOException {
	   File temp = copyToTempFromJar(path,loadClass);
       return Native.loadLibrary(temp.getAbsolutePath(), loadClass);
   }
   /**
    * copy file from current JAR archive to system temporary directory
    * 
    * The file from JAR is copied into system temporary directory and then loaded. The temporary file is deleted after
    * exiting.
    * Method uses String as filename because the pathname is "abstract", not system-dependent.
    * 
    * @param path The path of file inside JAR as absolute path (beginning with '/'), e.g. /package/File.ext
    * @param loadClass class that provide {@link ClassLoader} to load library file by input stream,if null, current class instead.
    * @throws IOException If temporary file creation or read/write operation fails
    * @throws IllegalArgumentException If the path is not absolute or if the filename is shorter than three characters
    * (restriction of {@link File#createTempFile(java.lang.String, java.lang.String)}).
    * @throws FileNotFoundException If the file could not be found inside the JAR.
    */
   public static synchronized File copyToTempFromJar(String path, Class<?> loadClass) throws IOException {
       if (null == path || !path.startsWith("/")) {
           throw new IllegalArgumentException("The path " + path + " has to be absolute (start with '/').");
       }

       // Prepare temporary file
       if (temporaryDir == null) {
           temporaryDir = createTempDirectory(NATIVE_FOLDER_PATH_PREFIX);
           temporaryDir.deleteOnExit();
       }

       File temp = new File(temporaryDir, path);
       Class<?> clazz = loadClass == null ? NativeUtils.class	: loadClass;
       InputStream is = clazz.getResourceAsStream(path);
       try {
           copy(is, temp);
           temp.deleteOnExit();
           return temp;
       } catch (IOException e) {
           temp.delete();
           throw e;
       } catch (NullPointerException e) {
           temp.delete();
           throw new FileNotFoundException("File " + path + " was not found inside JAR.");
       } finally {
    	   if (is != null) {
        	   is.close();
           }
       }       
   }
   /**
	 * @see #copyToTempFromJar(String, Class)
    */
   public static File copyToTempFromJar(String path) throws IOException {
	   return copyToTempFromJar(path, null);
   }
   /**
    * 从jar包中加载指定的动态库
    * @param path
    * @throws IOException
    * @see #loadLibraryFromJar(String, Class)
    */
   public static Object loadLibraryFromJar(String path) throws IOException {
	   return loadLibraryFromJar(path, null);
   }
   private static File createTempDirectory(String prefix) throws IOException {
       String tempDir = System.getProperty("java.io.tmpdir");
       File generatedDir = new File(tempDir, prefix + System.nanoTime());
       if (!generatedDir.mkdir())
           throw new IOException("Failed to create temp directory " + generatedDir.getName());
       
       return generatedDir;
   }
   /**
    * 从jar包中加载指定名字的动态库<br>
	 * 先尝试寻找'/libs/${name}',找不到再尝试'/${name}'
    * @param name 库名
    * @throws IOException
    * @see #loadLibraryFromJar(String, Class)
    */
	public static Object loadFromJar(String name) throws IOException {
		return loadFromJar(name, null);
	}
	public static Object loadFromJar(String name, Class<?> loadClass) throws IOException {
		String path = System.mapLibraryName(name);
		try {
		   	return loadLibraryFromJar("/libs/" + path, loadClass);
		} catch (FileNotFoundException e) {
			return loadLibraryFromJar("/" + path, loadClass);
		}
	}
   	public static File getTemporaryDir() {
		return temporaryDir;
	}
   
	private static long copy(InputStream in, File target) throws IOException  {
		target.delete();
		File parent = target.getParentFile();
		if (parent != null){
		   	parent.mkdirs();
		}
		OutputStream out = new FileOutputStream(target);
		try {
			return copy(in, out);
		} finally {
			out.close();
		}
   }
   /**
    * Reads all bytes from an input stream and writes them to an output stream.
    */
   private static long copy(InputStream source, OutputStream dest) throws IOException {
       long nread = 0L;
       byte[] buf = new byte[BUFFER_SIZE];
       int n;
       while ((n = source.read(buf)) > 0) {
           dest.write(buf, 0, n);
           nread += n;
       }
       return nread;
   }
}